/*
 * Copyright 2011 myTDev.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mytdev.application.splash;

import com.mytdev.application.Application.Initialization;
import com.mytdev.application.InitializationListener;
import com.mytdev.commons.action.Action.Result;
import com.mytdev.commons.util.messages.Message;
import java.awt.Graphics;
import java.awt.Image;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutionException;
import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author Yann D'Isanto
 */
public final class SplashFrame extends javax.swing.JFrame {

    private static final String SPLASH = "/META-INF/application/splash.png";
    private static final Logger LOG = LoggerFactory.getLogger(SplashFrame.class);
    private static ResourceBundle resources;
    private InitializationWorker worker;
    private Image splash;
    private static final Object INIT_ENDED = new Object();
    private static SplashFrame splashFrameInstance;

    /** Creates new form SplashFrame */
    private SplashFrame(Initialization initialization) {
        resources = ResourceBundle.getBundle(SplashFrame.class.getName());
        URL splashUrl = SplashFrame.class.getResource(SPLASH);
        if (splashUrl == null) {
            throw new IllegalArgumentException(resources.getString(r_strings.SPLASH_NOT_AVAILABLE));
        }
        setContentPane(new ImagePanel());
        initComponents();
        setTitle(resources.getString(r_strings.WINDOW_TITLE));
        setLocationRelativeTo(null);
        try {
            splash = ImageIO.read(splashUrl);
            setSize(splash.getWidth(null), splash.getHeight(null));
            setLocationRelativeTo(null);
        } catch (IOException ex) {
            String message = String.format(
                    resources.getString(r_strings.SPLASH_NOT_FOUND_MESSAGE), 
                    ex.getLocalizedMessage());
            LOG.error(message, ex);
        }
        worker = new InitializationWorker(this, initialization);
    }

    public static boolean isAvailable() {
        return SplashFrame.class.getResource(SPLASH) != null;
    }

    private void error(String title, String message) {
        JOptionPane.showMessageDialog(this, message, title, JOptionPane.ERROR_MESSAGE);
    }

    public static void launch(final Initialization initialization) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                splashFrameInstance = new SplashFrame(initialization);
                splashFrameInstance.setVisible(true);
                splashFrameInstance.worker.execute();
            }
        });
        synchronized (INIT_ENDED) {
            try {
                INIT_ENDED.wait();
                if (splashFrameInstance != null) {
                    splashFrameInstance.worker.get();
                    splashFrameInstance.dispose();
                }
            } catch (InterruptedException ex) {
                LOG.error(ex.getLocalizedMessage(), ex);
            } catch (ExecutionException ex) {
                LOG.error(ex.getLocalizedMessage(), ex);
            }
        }
    }

    private class ImagePanel extends JPanel {

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (splash != null) {
                g.drawImage(splash, 0, 0, null);
            }
        }
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        progressBar = new javax.swing.JProgressBar();
        statusLabel = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setUndecorated(true);

        progressBar.setStringPainted(true);

        statusLabel.setText("--");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 402, Short.MAX_VALUE)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(statusLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 382, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap(278, Short.MAX_VALUE)
                .addComponent(statusLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JProgressBar progressBar;
    private javax.swing.JLabel statusLabel;
    // End of variables declaration//GEN-END:variables

    private interface r_strings {

        String WINDOW_TITLE = "window.title";
        String SPLASH_NOT_AVAILABLE = "splash.not.found";
        String SPLASH_NOT_FOUND_MESSAGE = "splash.not.found";
        String LAUNCHING_MAINVIEW = "launching.mainview";
        String LOADING_APPENGINE = "loading.appengine";
        String LOADING_MAINVIEW = "loading.mainview";
        String LOADING_SERVICE = "loading.service";
        String INIT_COMPLETED = "init.completed";
        String ERROR_LOADING_APPENGINE_TITLE = "error.loading.appengine.title";
        String ERROR_LOADING_APPENGINE_MESSAGE = "error.loading.appengine.message";
        String ERROR_LOADING_MAINVIEW_TITLE = "error.loading.mainview.title";
        String ERROR_LOADING_MAINVIEW_MESSAGE = "error.loading.mainview.message";
        String ERROR_LOADING_SERVICE_TITLE = "error.loading.service.title";
        String ERROR_LOADING_SERVICE_MESSAGE = "error.loading.service.message";
        String ERROR_UNKNOWN = "error.unknown";
    }

    private static final class InitializationWorker extends SwingWorker<Result, Message> implements InitializationListener {

        private Initialization initialization;
        private SplashFrame splashFrame;

        public InitializationWorker(final SplashFrame splashFrame, Initialization initialization) {
            this.splashFrame = splashFrame;
            this.initialization = initialization;
            addPropertyChangeListener(new PropertyChangeListener() {

                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    if ("progress".equals(evt.getPropertyName())) {
                        splashFrame.progressBar.setValue((Integer) evt.getNewValue());
                    }
                }
            });
        }

        @Override
        protected Result doInBackground() throws Exception {
            initialization.addInitializationListener(this);
            LOG.info("start initialization run");
            initialization.run();
            LOG.info("initialization run finished");
            initialization.removeInitializationListener(this);
            Result result = initialization.getResult();
//            InitState result = initialization.getResult();
            return result;
        }

        @Override
        protected void process(List<Message> messages) {
            for (Message message : messages) {
                switch (message.getType()) {
                    case ERROR:
                        splashFrame.error(message.getTitle(), message.getMessage());
                        break;
                    case INFORMATION:
                        splashFrame.statusLabel.setText(message.getMessage());
                        break;
                    case WARNING:
                        splashFrame.statusLabel.setText(message.getMessage());
                        break;
                }
            }
        }

        @Override
        protected void done() {
            synchronized (INIT_ENDED) {
                INIT_ENDED.notifyAll();
            }
        }

        @Override
        public void launchingMainView() {
            LOG.debug("launching main view");
            publish(Message.info(null, resources.getString(r_strings.LAUNCHING_MAINVIEW)));
            setProgress(100);
        }

        @Override
        public void loadingMainView() {
            LOG.debug("loading main view");
            publish(Message.info(null, resources.getString(r_strings.LOADING_MAINVIEW)));
        }

        @Override
        public void loadingService(String service) {
            LOG.debug("loading service " + service);
            String message = String.format(
                    resources.getString(r_strings.LOADING_SERVICE), 
                    service);
            publish(Message.info(null, message));
        }

        @Override
        public void initializationCompleted() {
            LOG.debug("initialization completed");
            publish(Message.info(null, resources.getString(r_strings.INIT_COMPLETED)));
        }

        @Override
        public void errorOnLoadingAppEngine(Exception ex) {
            String title = resources.getString(r_strings.ERROR_LOADING_APPENGINE_TITLE);
            String message = String.format(
                    resources.getString(r_strings.ERROR_LOADING_APPENGINE_MESSAGE), 
                    ex.getLocalizedMessage());
            publish(Message.error(title, message));
        }

        @Override
        public void loadingAppEngine() {
            LOG.debug("loading application engine");
            publish(Message.info(null, resources.getString(r_strings.LOADING_APPENGINE)));
        }

        @Override
        public void errorOnLoadingMainView(Exception ex) {
            String title = resources.getString(r_strings.ERROR_LOADING_MAINVIEW_TITLE);
            String message = String.format(
                    resources.getString(r_strings.ERROR_LOADING_MAINVIEW_MESSAGE), 
                    ex.getLocalizedMessage());
            publish(Message.error(title, message));
        }

        @Override
        public void errorOnLoadingService(String service, Exception ex) {
            String title = resources.getString(r_strings.ERROR_LOADING_SERVICE_TITLE);
            String message = String.format(
                    resources.getString(r_strings.ERROR_LOADING_SERVICE_MESSAGE), 
                    service,
                    ex.getLocalizedMessage());
            publish(Message.error(title, message));
        }

        @Override
        public void appEngineLoaded() {
            setProgress(30);
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    splashFrame.setTitle(resources.getString(r_strings.WINDOW_TITLE));
                }
            });
        }

        @Override
        public void mainViewLoaded() {
            setProgress(99);
        }

        @Override
        public void serviceLoaded(String service) {
            int serviceProgress = (60 / initialization.getServiceCount()) * initialization.getLoadedServiceCount();
            setProgress(30 + serviceProgress);
        }
    }

//    private class R {
//        
//        String get(String key) {
//            res
//        }
//    }
}
